package bean;

import static java.util.Locale.ENGLISH;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;

import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;

import util.ClassHelper;

/**
 * <描述> Bean的操作类，单例
 * 
 * @author 王永弟
 * @since version 1.0.0
 * @copyright 广州从兴电子开发有限公司-电子渠道业务部
 * @initialize <实例化方法> BeanUtilsBean.getInstance()
 * @date 2011-5-12
 * @log <重大修改日志，格式：YYYY-MM-DD 修改人 修改内容>
 */
public class BeanUtilsBean {
	private static BeanUtilsBean bean = null;
	private final Map<String, Method[]> methodMap;
	private boolean useCache = true;

	private BeanUtilsBean() {
		methodMap = Collections.synchronizedMap(new WeakHashMap<String, Method[]>());
	}

	/**
	 * <方法描述> 获取对象实例
	 * 
	 * @return 实例
	 * @example <调用范例>
	 */
	public static BeanUtilsBean getInstance() {
		if (bean == null) {
			synchronized (BeanUtilsBean.class) {
				bean = new BeanUtilsBean();
			}
		}
		return bean;
	}

	/**
	 * <方法描述> 根据属性名，获取相应的属性类
	 * 
	 * @param beanClass
	 *            类名
	 * @param fieldName
	 *            属性名，可支持多级，如sid,group.sid
	 * @return 字段对应的类型
	 * @throws NoSuchFieldException
	 * @throws SecurityException
	 * @example <调用范例>
	 */
	public Class<?> getFieldType(Class<?> beanClass, String fieldName) throws SecurityException, NoSuchFieldException {
		int index = fieldName.indexOf(".");
		if (index > 0) {
			return getFieldType(getFieldType(beanClass, fieldName.substring(0, index)), fieldName.substring(index + 1));
		}
		Field f = ReflectionUtils.findField(beanClass, fieldName);
		return f.getType();
	}

	/**
	 * <方法描述> 获取属性的getter函数名
	 * 
	 * @param beanClass
	 *            类名
	 * @param fieldName
	 *            属性名，不支持多级属性
	 * @return getter函数名
	 * @throws NoSuchFieldException
	 * @throws SecurityException
	 * @example <调用范例>
	 */
	private String getGetterName(Class<?> beanClass, String fieldName) throws SecurityException, NoSuchFieldException {
		Class<?> type = getFieldType(beanClass, fieldName);
		if (boolean.class.equals(type)) {
			if (fieldName.startsWith("is") && fieldName.charAt(2) >= 'A' && fieldName.charAt(2) <= 'Z') {
				return fieldName;
			}
			return "is" + capitalize(fieldName);
		}
		return "get" + capitalize(fieldName);
	}

	/**
	 * <方法描述> 获取属性的setter函数名
	 * 
	 * @param beanClass
	 *            类名
	 * @param fieldName
	 *            属性名，不支持多级
	 * @return setter函数名
	 * @throws NoSuchFieldException
	 * @throws SecurityException
	 * @example <调用范例>
	 */
	private String getSetterName(Class<?> beanClass, String fieldName) throws SecurityException, NoSuchFieldException {
		Class<?> type = getFieldType(beanClass, fieldName);
		if (boolean.class.equals(type) && fieldName.startsWith("is") && fieldName.charAt(2) >= 'A'
				&& fieldName.charAt(2) <= 'Z') {
			return "set" + fieldName.substring(2);
		}
		return "set" + capitalize(fieldName);
	}

	/**
	 * <方法描述> 将字符串的首字段改为大写
	 * 
	 * @param fieldName
	 *            属性名，不支持多级
	 * @return 返回首字段大写的属性名
	 * @example <调用范例>
	 */
	private String capitalize(String fieldName) {
		if (fieldName == null || fieldName.length() == 0) {
			return fieldName;
		}
		return fieldName.substring(0, 1).toUpperCase(ENGLISH) + fieldName.substring(1);
	}

	/**
	 * <方法描述> 获取类属性的setter方法，对于多级属性名，按顺序返回多个Method<br>
	 * 如group.sid，返回两个Method，Method[0]为group的setter方法，Method[1]为sid的setter方法
	 * 
	 * @param beanClass
	 *            类名
	 * @param fieldName
	 *            属性名，可支持多级，如sid,group.sid
	 * @return setter方法
	 * @example <调用范例>
	 */
	public Method[] getSetterMethod(Class<?> beanClass, String fieldName) {
		Method[] method = null;
		if (useCache) {
			method = methodMap.get(beanClass.getName() + "." + fieldName + "_setter");
		}
		if (method == null) {
			String[] temp = fieldName.split("\\.");
			method = new Method[temp.length];
			Class<?> cls = beanClass;
			try {
				for (int i = 0; i < temp.length; i++) {
					String methodName = getSetterName(cls, temp[i]);
					Class<?> paramType = getFieldType(cls, temp[i]);
					method[i] = ReflectionUtils.findMethod(cls, methodName, new Class<?>[] { paramType });
					cls = method[i].getParameterTypes()[0];
				}
			} catch (Exception e) {
				throw new BeanParseException("Get setter method error!", e);
			}
			if (useCache) {
				methodMap.put(new String(beanClass.getName() + "." + fieldName + "_setter"), method);
			}
		}
		return method;
	}

	/**
	 * <方法描述> 获取类属性的getter方法，对于多级属性名，按顺序返回多个Method<br>
	 * 如group.sid，返回两个Method，Method[0]为group的getter方法，Method[1]为sid的getter方法
	 * 
	 * @param beanClass
	 *            类名
	 * @param fieldName
	 *            属性名，可支持多级，如sid,group.sid
	 * @return getter方法
	 * @example <调用范例>
	 */
	public Method[] getGetterMethod(final Class<?> beanClass, final String fieldName) {
		Method[] method = null;
		if (useCache) {
			method = methodMap.get(beanClass.getName() + "." + fieldName + "_getter");
		}
		if (method == null) {
			String[] temp = fieldName.split("\\.");
			method = new Method[temp.length];
			Class<?> cls = beanClass;
			try {
				for (int i = 0; i < temp.length; i++) {
					String methodName = getGetterName(cls, temp[i]);
					method[i] = ReflectionUtils.findMethod(cls, methodName);
					cls = method[i].getReturnType();
				}
			} catch (Exception e) {
				throw new BeanParseException("Get getter method error!", e);
			}
			if (useCache) {
				methodMap.put(new String(beanClass.getName() + "." + fieldName + "_getter"), method);
			}
		}
		return method;
	}

	/**
	 * <方法描述> 获取当个setter方法，当前方法仅在当前属性中，不支持多级
	 * 
	 * @param beanClass
	 *            类名
	 * @param fieldName
	 *            属性名，不支持多级
	 * @return setter方法
	 * @throws NoSuchFieldException
	 * @throws SecurityException
	 * @throws NoSuchMethodException
	 * @example <调用范例>
	 */
	public Method getSingleSetterMethod(final Class<?> beanClass, final String fieldName) throws SecurityException,
			NoSuchFieldException, NoSuchMethodException {
		Method method = null;
		String methodName = getSetterName(beanClass, fieldName);
		method = ReflectionUtils.findMethod(beanClass, methodName);
		return method;
	}

	/**
	 * <方法描述> 获取当个getter方法，当前方法仅在当前属性中，不支持多级
	 * 
	 * @param beanClass
	 *            类名
	 * @param fieldName
	 *            属性名，不支持多级
	 * @return getter方法
	 * @throws NoSuchMethodException
	 * @throws SecurityException
	 * @throws NoSuchFieldException
	 * @example <调用范例>
	 */
	public Method getSingleGetterMethod(final Class<?> beanClass, final String fieldName) throws SecurityException,
			NoSuchMethodException, NoSuchFieldException {
		Method method = null;
		String methodName = getGetterName(beanClass, fieldName);
		method = ReflectionUtils.findMethod(beanClass, methodName);
		return method;
	}

	/**
	 * <方法描述> 获取属性值，支持多级属性值的获取
	 * 
	 * @param bean
	 *            对象
	 * @param fieldName
	 *            属性名，支持多级获取，如获取group.sid的值
	 * @return 属性值
	 * @example <调用范例>
	 */
	public Object getProperty(final Object bean, final String fieldName) {
		Assert.hasLength(fieldName, "属性名不能为空！");
		Method[] getters = getGetterMethod(bean.getClass(), fieldName);
		return getProperty(bean, getters);
	}

	/**
	 * <方法描述> 获取属性名，根据多个方法获取，获取的值为最后一个方法对应的值
	 * 
	 * @param bean
	 *            对象值
	 * @param method
	 *            方法，第一个方法为bean对象里的方法
	 * @return 属性值
	 * @example <调用范例>
	 */
	public Object getProperty(final Object bean, final Method... method) {
		Assert.notNull(bean);
		try {
			Object obj = bean;
			for (Method m : method) {
				obj = m.invoke(obj);
				if (obj == null)
					break;
			}
			return obj;
		} catch (Exception e) {
			throw new BeanParseException("get property error!", e);
		}
	}

	/**
	 * <方法描述> 获取Int型属性值，支持多级属性值的获取，当对象为空时，返回0
	 * 
	 * @param bean
	 *            对象
	 * @param fieldName
	 *            属性名，支持多级获取，如获取group.sid的值
	 * @return int型属性值
	 */
	public int getIntProperty(Object bean, String fieldName) {
		Object obj = getProperty(bean, fieldName);
		if (obj != null)
			return (Integer) obj;
		return 0;
	}

	/**
	 * <方法描述> 将String的值，存放入bean的字段里
	 * 
	 * @param bean
	 *            数据bean
	 * @param fieldName
	 *            字段名称，如：sid,group.sid
	 * @param value
	 *            字符串值，会自动根据数据模型中的类型，来转换
	 * @throws NoSuchFieldException
	 * @throws SecurityException
	 * @throws InstantiationException
	 * @throws InvocationTargetException
	 * @throws IllegalAccessException
	 * @throws IllegalArgumentException
	 * @example <调用范例> <br>
	 *          Attribute attribute = new Attribute(); <br>
	 *          BeanUtilsBean.getInstance().setProperty(attribute,"sid","111"); <br>
	 *          BeanUtilsBean.getInstance().setProperty(attribute,"group.sortNum","1");
	 */
	public void setProperty(Object bean, String fieldName, String value) throws SecurityException,
			NoSuchFieldException, IllegalArgumentException, IllegalAccessException, InvocationTargetException,
			InstantiationException {
		Class<?> type = getFieldType(bean.getClass(), fieldName);
		Object val = ConvertUtils.convert(value, type);
		setProperty(bean, fieldName, val);
	}

	/**
	 * <方法描述> 设置属性值，支持多级属性名
	 * 
	 * @param bean
	 *            对象值
	 * @param fieldName
	 *            属性名，支持多级，如支持group.sid的设值方式
	 * @param value
	 *            属性值
	 * @throws InstantiationException
	 * @throws InvocationTargetException
	 * @throws IllegalAccessException
	 * @throws IllegalArgumentException
	 * @example <调用范例>
	 */
	public void setProperty(Object bean, String fieldName, Object value) throws IllegalArgumentException,
			IllegalAccessException, InvocationTargetException, InstantiationException {
		Assert.notNull(bean);
		Assert.hasLength(fieldName);
		Method[] getters = getGetterMethod(bean.getClass(), fieldName);
		Method[] setters = getSetterMethod(bean.getClass(), fieldName);
		setProperty(bean, value, getters, setters);
	}

	/**
	 * <方法描述> 根据方法设置属性值，仅限单级，即当前Method为bean里面的方法
	 * 
	 * @param bean
	 *            对象值
	 * @param method
	 *            setter方法
	 * @param value
	 *            属性值
	 * @throws InvocationTargetException
	 * @throws IllegalAccessException
	 * @throws IllegalArgumentException
	 * @example <调用范例>
	 */
	public void setProperty(Object bean, Method method, Object value) throws IllegalArgumentException,
			IllegalAccessException, InvocationTargetException {
		Assert.notNull(bean);
		method.invoke(bean, value);
	}

	/**
	 * <方法描述> 根据方法设置属性值，支持多级设值，要求getters/setters中的方法个数一致
	 * 
	 * @param bean
	 *            对象实例
	 * @param value
	 *            属性值
	 * @param getters
	 *            getter方法数组，按属性级排序，如group.sid，应该有两个getter方法，Method[0]为group的getter方法，Method[1]
	 *            为sid的getter方法
	 * @param setters
	 *            setter方法数组，与getter的方法个数相同
	 * @throws InvocationTargetException
	 * @throws IllegalAccessException
	 * @throws IllegalArgumentException
	 * @throws InstantiationException
	 * @example <调用范例>
	 */
	public void setProperty(Object bean, Object value, Method[] getters, Method[] setters)
			throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, InstantiationException {
		Assert.notNull(bean);
		Object parent = bean;
		for (int i = 0; i < setters.length; i++) {
			if (i == setters.length - 1) {
				setters[i].invoke(parent, value);
			} else {
				Object subObj = getters[i].invoke(parent);
				if (subObj == null) {
					subObj = getters[i].getReturnType().newInstance();
					setters[i].invoke(parent, subObj);
					parent = subObj;
				} else {
					parent = subObj;
				}
			}
		}
	}

	/**
	 * <方法描述> 将属性值放到bean中
	 * 
	 * @param bean
	 *            对象实例
	 * @param properties
	 *            属性Map
	 * @throws InstantiationException
	 * @throws InvocationTargetException
	 * @throws IllegalAccessException
	 * @throws IllegalArgumentException
	 * @throws NoSuchFieldException
	 * @throws SecurityException
	 */
	public void populate(Object bean, Map<String, String> properties) throws IllegalArgumentException,
			IllegalAccessException, InvocationTargetException, InstantiationException, SecurityException,
			NoSuchFieldException {
		Assert.notNull(bean);
		Assert.notEmpty(properties);
		for (Map.Entry<String, String> entry : properties.entrySet()) {
			String name = entry.getKey();
			if (name == null) {
				continue;
			}
			setProperty(bean, name, entry.getValue());
		}
	}

	/**
	 * <方法描述>
	 * 
	 * @param beans
	 * @param fieldName
	 * @param values
	 * @throws InstantiationException
	 * @throws InvocationTargetException
	 * @throws IllegalAccessException
	 * @throws IllegalArgumentException
	 * @example <调用范例>
	 */
	public void setProperty(Object[] beans, String fieldName, Object[] values) throws IllegalArgumentException,
			IllegalAccessException, InvocationTargetException, InstantiationException {
		Assert.notEmpty(beans);
		Assert.hasLength(fieldName);
		Assert.notEmpty(values);
		if (beans.length != values.length) {
			throw new IllegalArgumentException("beans.lenth is not equals values.length");
		}
		Class<?> beanClass = beans[0].getClass();
		Method[] setters = getSetterMethod(beanClass, fieldName);
		Method[] getters = getGetterMethod(beanClass, fieldName);
		for (int i = 0; i < beans.length; i++) {
			setProperty(beans[i], values[i], getters, setters);
		}
	}

	/**
	 * <方法描述> 判断对象中是否函数该字段
	 * 
	 * @param clazz
	 *            对象类
	 * @param fieldName
	 *            属性名
	 * @return true or false
	 */
	public boolean hasField(Class<?> clazz, String fieldName) {
		Assert.notNull(clazz);
		Assert.hasLength(fieldName);
		try {
			clazz.getDeclaredField(fieldName);
		} catch (Exception e) {
			Class<?> superClazz = clazz.getSuperclass();
			if (superClazz != null) {
				return hasField(superClazz, fieldName);
			}
			return false;
		}
		return true;
	}

	/**
	 * <方法描述> 是否使用缓存
	 * 
	 * @return true or false
	 */
	public boolean isUseCache() {
		return useCache;
	}

	/**
	 * <方法描述> 设置是否采用缓存，默认采用缓存
	 * 
	 * @param useCache
	 *            true or false
	 */
	public void setUseCache(boolean useCache) {
		this.useCache = useCache;
	}

	/**
	 * <方法描述> 根据属性类型，获取属性的所有值对象，包含指定类型的数组
	 * 
	 * @param <T>
	 * @param bean
	 * @param type
	 * @return
	 */
	@SuppressWarnings( { "unchecked", "rawtypes" })
	public <T> List<T> getObjectsByType(Object bean, Class<T> type) {
		List<T> list = new ArrayList<T>();
		for (Field field : bean.getClass().getDeclaredFields()) {
			Class cls = field.getType();
			if (cls.equals(type)) {
				T obj = (T) getProperty(bean, field.getName());
				if (obj != null)
					list.add(obj);
			} else if (cls.isArray() && cls.getComponentType().equals(type)) {
				T[] objects = (T[]) getProperty(bean, field.getName());
				if (objects != null) {
					for (T obj : objects) {
						if (obj != null)
							list.add(obj);
					}
				}
			} else if (ClassHelper.isAssignable(Collection.class, cls)) {
				Collection objects = (Collection) getProperty(bean, field.getName());
				if (objects != null) {
					for (Object obj : objects) {
						if (obj != null && obj.getClass().equals(type)) {
							list.add((T) obj);
						}
					}
				}
			}
		}
		return list;
	}

	public String beanToString(Object object) {
		StringBuffer sb = new StringBuffer();
		if (object != null) {
			Field[] f = object.getClass().getDeclaredFields();
			try {
				for (int i = 0; i < f.length; i++) {
					// System.out.println("Type:" + f[i].getType());
					boolean accessFlag = f[i].isAccessible();
					f[i].setAccessible(true);
					if (i != 0)
						sb.append("|");
					sb.append(f[i].getName());
					sb.append("=");
					sb.append(f[i].get(object));
					f[i].setAccessible(accessFlag);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return sb.toString();
	}
}
